package org.xurui.bsj.protocol.codec;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetlinks.core.device.DeviceRegistry;
import org.jetlinks.core.message.*;
import org.jetlinks.core.message.codec.*;
import org.jetlinks.core.message.function.FunctionInvokeMessage;
import org.jetlinks.core.message.property.WritePropertyMessage;
import org.jetlinks.core.server.session.DeviceSession;
import org.jetlinks.core.spi.ServiceContext;
import org.reactivestreams.Publisher;
import org.springframework.util.StringUtils;
import org.xurui.bsj.protocol.command.CmdCodec;
import org.xurui.bsj.protocol.interceptor.MyDeviceMessageSenderInterceptor;
import org.xurui.bsj.protocol.msg.BsjMessage;
import org.xurui.bsj.protocol.msg.BsjMessageBuilder;
import org.xurui.bsj.protocol.provider.CmdCodecProvider;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


/**
 * 上学啦设备编解码
 */
@Slf4j
public class TcpMessageCodec implements DeviceMessageCodec  {

    private ServiceContext serviceContext;

    private MyDeviceMessageSenderInterceptor senderInterceptor;

    private DeviceRegistry deviceRegistry;

    public TcpMessageCodec(ServiceContext serviceContext, MyDeviceMessageSenderInterceptor senderInterceptor, Optional<DeviceRegistry> registry){
        super();
        this.serviceContext = serviceContext;
        this.senderInterceptor = senderInterceptor;
        this.deviceRegistry = registry.get();
    }
    // 传输协议定义
    @Override
    public Transport getSupportTransport() {
        return DefaultTransport.TCP;
    }

    // 把tcp消息解码为平台消息，多用于设备上报消息到平台
    @Override
    public Publisher<? extends Message> decode(MessageDecodeContext context) {
//        log.debug("博实结开始执行解码");
        return Flux.defer(() -> {
            // 消息上下文
            FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context);
            // 从上下文中获取消息字节数组
            ByteBuf byteBuf = context.getMessage().getPayload();
            byte[] payload = ByteBufUtil.getBytes(byteBuf, 0, byteBuf.readableBytes(), false);
//            log.debug("博实结二进制数据{}",parseByte2HexStr(payload));
            // 把字节流转换为字符串，根据不同设备不同协议进行解析，
            String text=new String(payload);
//            if (log.isDebugEnabled()) {
//                log.debug("博实结上报消息内容{}",text);
//            }
            BsjMessage msg = BsjMessageBuilder.create().buildBsjMessage(text);
//            if (log.isDebugEnabled()) {
                log.debug("博实结上报消息解密后内容{}",msg);
//            }
            List<DeviceMessage> messages = new ArrayList<>();
            boolean needReply = false;
            if(msg!=null){
                try{
                    CmdCodec cmdCodec = CmdCodecProvider.getInstance().getCmdCodecByCmd(msg.getHeader().getCmd());
                    if(cmdCodec!=null){
                        messages.addAll(cmdCodec.parsePayload(msg,ctx));
                        if(3 == msg.getHeader().getType()){
                            if(!msg.isWithOutRelayToDevice()){
                                if(StringUtils.isEmpty(msg.getTempReplyBody())){
                                    log.error("博实结消息需要回复，但是解码器里面没设置回复信息   上报消息【{}】",text);
                                }else{
                                    needReply = true;
                                }
                            }
                        }
                    }else{
                        log.error("异常-博实结命令【{}】没找到解码器 消息内容为【{}】",msg.getHeader().getCmd(),msg);
                    }
                }catch (Exception e){
                    log.error("异常-博实结解码消息异常",e);
                    throw new RuntimeException(e);
                }

            }
           // 获取设备会话信息
           DeviceSession session = ctx.getSession();
            if(needReply){
                EncodedMessage encodedMessage = BsjMessageBuilder.create().bulidBsjCommonReplyMessage(msg);
                if(encodedMessage!=null){
                    session.send(encodedMessage).subscribe();
                }
            }
           // 如果session中没有设备信息，则为设备首次上线
           if (session.getOperator() == null) {
               DeviceOnlineMessage onlineMessage = new DeviceOnlineMessage();
               onlineMessage.setDeviceId(messages.get(0).getDeviceId());
               onlineMessage.setTimestamp(System.currentTimeMillis());
               // 返回到平台上线消息
               messages.add(onlineMessage);
               if(deviceRegistry !=null){
                   deviceRegistry.getDevice(onlineMessage.getDeviceId())
                       .doOnNext(deviceOperator -> {
                           senderInterceptor.sendWillDeviceMsgDelay(deviceOperator);
                       }).flatMap(deviceOperator -> deviceOperator.setConfig("iccid",msg.getHeader().getIccid()))
                       .subscribe();
               }
           }
           DeviceLogMessage logMessage = new DeviceLogMessage();
           logMessage.setLog(text);
           messages.add(logMessage);
           return Flux.fromIterable(messages);
        });
    }

    // 把平台消息编码为协议传输消息，多用于平台命令下发到设备
    @Override
    public Publisher<? extends EncodedMessage> encode(MessageEncodeContext context) {
        // 从平台消息上下文中获取消息内容
        CommonDeviceMessage message = (CommonDeviceMessage) context.getMessage();


        log.info("博实结下发原始内容{}",message);
        return Mono.just(message)
            .flatMap(item->{
                return context.getDevice(message.getDeviceId());
            }).flatMap(deviceOperator -> {
                return deviceOperator.getConfig("iccid");
            }).flatMap(iccid->{
                message.setCode(iccid.asString());
                String cmd = null;
                //1处理命令消息
                if(message instanceof FunctionInvokeMessage){
                    FunctionInvokeMessage msg = (FunctionInvokeMessage) message;
                    cmd = msg.getFunctionId();
                }
                if(message instanceof WritePropertyMessage){
                    WritePropertyMessage msg = (WritePropertyMessage) message;
                    cmd = msg.getProperties().get("cmd").toString();
                }
                if(cmd!=null){
                    CmdCodec cmdCodec =  CmdCodecProvider.getInstance().getCmdCodecByCmd(cmd);
                    if(cmdCodec!=null){
                        return Mono.defer(()->{
                            EncodedMessage deviceMessage = cmdCodec.encodeMessage(message,iccid.asString());
                            return Mono.just(deviceMessage);
                        });
                    }else{
                        log.error("博实结命令【{}】没找到编码器 消息内容为【{}】",cmd,message.toString());
                    }
                }else{
                    log.error("博实结编码命令为空 消息内容为【{}】",message.toString());
                }
                return Mono.empty();
            });


//
//
//        EncodedMessage encodedMessage = EncodedMessage.simple(Unpooled.wrappedBuffer(message.toString().getBytes()));
//        // 根据消息类型的不同，构造不同的消息
//        if (message instanceof ReadPropertyMessage) {
//            ReadPropertyMessage readPropertyMessage = (ReadPropertyMessage) message;
//            // 获取需要传输的字节
//            byte[] bytes = readPropertyMessage.toString().getBytes();
//            // 构造为平台传输到设备的消息体
//            encodedMessage = EncodedMessage.simple(Unpooled.wrappedBuffer(bytes));
//        }
    }

    public String parseByte2HexStr(byte buf[]){
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<buf.length;i++){
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if(hex.length() == 1){
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

}
